// es6 --   http
// 自定义 http 请求函数
/*
    $request(assParams:String|Object[,data:String|Object])
    网络请求 基于 wx.request api
    参数字典
    assParams -- String|Object   #required  // 请求路径 或  配置参数
    {
        url: String 
        ,header: Object || [undefined,null,""]
        ,join: Boolean 
        ,loading: Object || Boolean || any
        ,serialize : Boolean
        ,serializeMethods: Array
    }
    data -- String | Object   // 请求参数 
    由于配置全局接口路径  
    需要请求其他url路径或其他请求配置请在 assParams 配置参数
    主意 assParams 为 Object时  默认进行浅合并  url默认拼接全局 api路径
    当 assParams 中 join 为 false  会直接替换
    loading 默认为一个对象 为了常用配置的省略   可以为  一个 boolean值  true 等于默认loading  false隐藏  复杂类型可以设置为对象
    url 必须为完整 api assParams 
    考虑异步性能与复杂性 请注意
    尽量忽略  success fail 回调  请按 promise调用
    方法 api 参考 axios
    新增  serialize
    // 是否对 post put -data ->params 为对象格式时进行json序列化
    serialize : Boolean
    // 序列化 method 方法
    serializeMethods:["POST","PUT"]
*/
/* 
    2021/9/10  更新
    
    1.  在App生命周期上报错的问题 ， 由于此时App处于在打包状态，无法获取当前视图page对象。 修改page获取不到的情况下为 null
    2. 在之前的逻辑中   默认http获取当前路由为page对象参数传入http拦截回调函数中
    ## 问题
    在当前视图中 有些情况需要操作路由栈中  其他路由对象 。 
    在此种情况下 ， 用其他路由对象调用原型的http方法 page默认还是指向当前视图路由  在特殊情况下有可能发生意向不到错误
    尽管在极大多数情况下，用于操作http拦截都会暴露在当前视图窗口。
    ## 修改方案
    page 可能为[   路由实例对象 , null  ]
    考虑多种情况  默认this  取 window对象  之后对后续操作没什么作用  所以改为 null  也好判断。
    逻辑顺序
        优先判断 this是否为 非顶级对象  如果不是 取 this  this为假值时 取null
        this为顶级对象时  判断当前视图实例是否存在  存在则取 当前视图实例  不存在取 null
*/
const comUtils = require("../../utils/common");
const httpConfig = require("./config");

function request(assParams, data = null, method = "GET") {
  /* 调用对象 */
  const thisCaller = this;
  return new Promise((resolve, reject) => {
    //  参数合并
    if (assParams) {
      let defaultOpts = {
        // 接口地址
        //url: "",
        // 请求方法
        method,
        // 传送数据
        data,
        // 默认 header 头配置
        header: {
          ["content-type"]: "application/x-www-form-urlencoded",
        },
        // 是否进行url参数拼接 默认拼接  |  false  直接替换
        join: true,
        // 是否显示 loading层  Boolean or Object  对象类型设置 loading配置参数--uni.showLoading
        // loading 弹框文字 { title:"loading..." }
        // , loading: true
        // 是否对 post put -data ->params 为对象格式时进行json序列化
        serialize: true,
        // 序列化 method 方法
        serializeMethods: ["POST", "PUT"],
      };
      // 请求拦截
      if (httpConfig.httpRequestIntercept) {
        defaultOpts = httpConfig.httpRequestIntercept(defaultOpts);
        if (defaultOpts) {
          throw Error("Request interception must return an object");
        }
      }

      /* 
                默认配置参数合并 ended
            */
      //如果默认配置参数  url已经存在  则取 url  否则取   Api
      const apiPath = defaultOpts.url || httpConfig.httpApi;
      // 获取 arg[0] 参数类型
      const optsType = comUtils.toClass(assParams);
      // arg[0]  url参数暂存变量
      let joinUrl;
      // 如果为字符串  则进行路径拼接
      if (optsType === "String") {
        joinUrl = assParams;
        //如果是 对象类型  则进行浅合并
      } else if (optsType === "Object") {
        joinUrl = assParams.url || "";
        delete assParams.url;
        Object.assign(defaultOpts, assParams);
      } else {
        throw new TypeError(" arg[0] params type is must be String or Object");
      }
      /* --  传入参数合并 ended */
      // default url join
      if (defaultOpts.join) {
        defaultOpts.url = apiPath + joinUrl;
      } else {
        defaultOpts.url = joinUrl;
      }
      // arg[1] http-header respones merge
      if (data) {
        defaultOpts.data = data;
      }

      const { url } = defaultOpts;
      if (url && comUtils.trimAll(url)) {
        /* default loading merge */
        let loadingAuto = false;
        const defaultLoading = { title: "加载中..." };
        let loading;
        if (!comUtils.hasOwn(defaultOpts, "loading")) {
          loading = defaultLoading;
        } else {
          loading = defaultOpts.loading;
        }
        if (loading) {
          if (loading === true) {
            wx.showLoading(defaultLoading);
            loadingAuto = true;
          }
          if (comUtils.toClass(loading) === "Object") {
            wx.showLoading(loading);
            loadingAuto = true;
          }
        }
        // 对 data params 进行JSON字符串序列化
        const { serialize } = defaultOpts;
        if (serialize) {
          const { method, serializeMethods = [], data } = defaultOpts;
          if (
            serializeMethods.includes(method) &&
            data &&
            comUtils.toClass(data) === "Object"
          ) {
            Object.keys(data).forEach((key) => {
              const val = data[key];
              const valType = comUtils.toClass(val);
              if (valType === "Object" || valType === "Array") {
                data[key] = JSON.stringify(val);
              }
            });
          }
        }
        const url_notic = joinUrl || url;
        wx.request(
          Object.assign(defaultOpts, {
            success(res) {
              if (loadingAuto) {
                wx.hideLoading({
                  fail() {
                    return false;
                  },
                });
              }
              /* 根据后台控制器修改 */
              const { statusCode, errMsg } = res;
              const statusReg = /^[23]0\d$/;
              if (statusReg.test(statusCode)) {
                const resData = res.data;
                const { code } = resData;
                let statusCode = Number(code);
                resData.statusCode = statusCode;
                function _next(data = resData, type = true) {
                  if (type) {
                    resolve(data);
                  } else {
                    reject(data);
                  }
                }
                // 异常状态码拦截回调
                if (
                  httpConfig.httpResponseIntercept &&
                  typeof httpConfig.httpResponseIntercept === "function"
                ) {
                  let paramsPage;
                  //考虑性能
                  // 大多数为  组件或者路由实例调用
                  if (thisCaller !== self) {
                    paramsPage = thisCaller || null;
                  } else {
                    // 如果是顶级对象
                    const pagesArr = getCurrentPages();
                    let curPage = null;
                    if (pagesArr && pagesArr.length) {
                      curPage = pagesArr[pagesArr.length - 1];
                    }
                    // 并且当前视图路由存在的情况下
                    if (curPage) {
                      paramsPage = curPage;
                    } else {
                      paramsPage = null;
                    }
                  }
                  httpConfig.httpResponseIntercept(
                    resData,
                    { page: paramsPage, url: url_notic },
                    _next
                  );
                } else {
                  // status success next
                  _next();
                }
              } else {
                reject(`HTTP Error( code ): [ ${url_notic} ] ${statusCode} `);
              }
            },
            fail(error) {
              if (loadingAuto) {
                wx.hideLoading({
                  fail() {
                    return false;
                  },
                });
              }
              reject(
                `HTTP Error( connect ):[ ${url_notic} ] ${
                  error.errMsg || "请求失败"
                }`
              );
            },
          })
        );
      } else {
        throw new Error("missing necessary parameters or type error of url");
      }
    } else {
      throw new ReferenceError("http params url must be required");
    }
  });
}

/* 
    $get(path:String|Object,data:String|Object);
    $get 与 $post 基于 $request方法
    参数类型与request相同
    get  请求
    不做请求方式强制绑定    留待可以扩展其他 post  put delect方式 

*/
function httpGet(path, data) {
  return request(path, data);
}
function httpPost(path, data) {
  return request(path, data, "POST");
}
function httpPut(path, data) {
  return request(path, data, "PUT");
}
function httpDelete(path, data) {
  return request(path, data, "DELETE");
}

module.exports = {
  httpGet,
  httpPost,
  httpPut,
  httpDelete,
};
